/**
 * @author chuwk
 * @param {object} datas           流程图展示数据，格式["nodeDataArray": [], "linkDataArray": []]
 * @param {string} diagramId       流程图id
 * @param {object} diagramScope    外部关联流程图内数据的对象，可以调用流程图组件中的方法/属性
 * @param {boolean} isFixedHeight  是否是固定高度 true：是  false: 自适应高度
 * @param {string} minHeight       固定高度时设置最小高度
 * @param {boolean} isShowVersion  是否显示版本号 true：显示
 * @param {object} version         版本号数据，格式{"name": "XXX", "version": "XXX"}
 * @param {string} paramsType      模式（current, online, compare）
 * @param {boolean} isReadonly     只读模式 true：只读
 * @param {function} clickFun      添加功能点/流程节点时使用
 * @param {function} blur          获取点击节点内容
 */
angular.module('app').component('winitFlowBuilder', {
    templateUrl: getTemplateUrl('matrix/component/tpl/winitFlowBuilder.html'),
    bindings: {
        datas: '<',
        diagramId: '@',
        diagramScope: '=',
        isFixedHeight: '<',
        minHeight: '@',
        isShowVersion: '<',
        version: '<',
        paramsType: '@',
        isReadonly: '<',
        clickFun: '&',
        blur: '&',
        reloadFlowFun: '&'
    },
    controller: function($scope, $stateParams, $element, $attrs, $timeout, $http, $rootScope, lodash) {
        var ctrl = this;
        console.log("获取传入的数据");
        console.log(ctrl.datas);

        //自适应高度
        ctrl.setTableHeight = function() {
            $timeout(function() {
                var h = document.documentElement.clientHeight || document.body.clientHeight;
                h = ctrl.isFixedHeight ? (ctrl.minHeight || (h - 300) / 2) : h - 255;
                if (document.getElementById(ctrl.diagramId)) {
                    document.getElementById(ctrl.diagramId).style.height = h + "px";
                }
            })
        }
        ctrl.setTableHeight();
        window.onresize = ctrl.setTableHeight;

        // 定义一个拖拽工具, 重新定义linkline的拖拽效果.
        function DragLinkingTool() {
            go.DraggingTool.call(this);
            this.isGridSnapEnabled = true;
            this.isGridSnapRealtime = false;
            this.gridSnapCellSize = new go.Size(20, 1);
            this.gridSnapOrigin = new go.Point(10, 0);
        }
        go.Diagram.inherit(DragLinkingTool, go.DraggingTool);

        /** @override */
        DragLinkingTool.prototype.doActivate = function() {
            var diagram = this.diagram;
            if (diagram === null) return;
            this.standardMouseSelect();
            var main = this.currentPart;
            if (main instanceof go.Link) {
                var relinkingtool = diagram.toolManager.relinkingTool;
                relinkingtool.originalLink = main;
                diagram.currentTool = relinkingtool;
                relinkingtool.doActivate();
                relinkingtool.doMouseMove();
            } else {
                go.DraggingTool.prototype.doActivate.call(this);
            }
        };

        var $ = go.GraphObject.make;

        function extend(des, src, override) {
            if (src instanceof Array) {
                for (var i = 0, len = src.length; i < len; i++)
                    extend(des, src[i], override);
            }
            for (var i in src) {
                if (override || !(i in des)) {
                    des[i] = src[i];
                }
            }
            return des;
        }

        // 1.定义绘图模板中使用到的通用样式的构造器, 方便统一修改
        var linkLineStyle = { stroke: "gray", strokeWidth: 2 };
        var menuButtonStyle = {
            alignment: go.Spot.Left,
            margin: new go.Margin(2, 2, 2, 2),
        };
        var temporaryLinkStyle = { stroke: "dodgerblue", strokeWidth: 5 };
        var graygradStyle = $(go.Brush, "Linear", { 0: "white", 0.1: "whitesmoke", 0.9: "whitesmoke", 1: "lightgray" });

        // 定义模板中文本风格, 通过config参数和默认值合并得到最终风格配置.
        var textBox = function(config) {
            var defConfig = {
                stroke: "black",
                editable: false,
                font: "13px sans-serif",
                margin: new go.Margin(3, 3 + 11, 3, 3 + 4),
                alignment: go.Spot.Left,
            };
            return extend(defConfig, config, true);
        }

        $timeout(function() {
            var ColumeInterval = 50;
            var nodeDataArrayLength = ctrl.datas ? (ctrl.datas.nodeDataArray ? ctrl.datas.nodeDataArray.length : 0) : 0;

            // 2.定义画布
            var myDiagram = $(go.Diagram, ctrl.diagramId, {
                allowCopy: false,
                "toolManager.mouseWheelBehavior":go.ToolManager.WheelNone,
                // initialScale: 0.7,
                initialScale: nodeDataArrayLength >= 5 ? 0.7 : 1.0, // 设置初始化页面大小
                initialContentAlignment: nodeDataArrayLength == 1 ? go.Spot.Left : go.Spot.Center,
                isReadOnly: ctrl.isReadonly || false,
                grid: $(go.Panel, "Grid", { gridCellSize: new go.Size(8, 8) },
                    $(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
                    $(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 })
                ),
                // layout: $(go.LayeredDigraphLayout, {
                //     setsPortSpots: false,
                //     columnSpacing: ColumeInterval,
                //     isInitial: false,
                //     isOngoing: false
                // }),
                validCycle: go.Diagram.CycleNotDirected,
                "undoManager.isEnabled": true, //支持ctrl+z, 非常实用
            });

            myDiagram.animationManager.isEnabled = false;

            // 2.小功能, 改动画布内容后, 浏览器标题出现"*"
            myDiagram.addDiagramListener("Modified", function(e) {
                var button = document.getElementById("SaveButton");
                if (button) button.disabled = !myDiagram.isModified;
                var idx = document.title.indexOf("(*改动)");
                if (myDiagram.isModified) {
                    if (idx < 0) document.title += "(*改动)";
                } else {
                    if (idx >= 0) document.title = document.title.substr(0, idx);
                }
            });

            // 2.小功能, 改动画布内容后, 浏览器标题出现"*"
            myDiagram.addDiagramListener("BackgroundSingleClicked", function(e) {
                myDiagram.toolManager.contextMenuTool.hideContextMenu();
            });

            var menuButton = function(lable, onClick, visible) {
                return $("ContextMenuButton",
                    $(go.TextBlock, menuButtonStyle, lable), { click: onClick },
                    new go.Binding("visible", "", visible).ofObject());
            }

            // myDiagram.isEnabled = false;

            // 3.右键菜单
            // 1 节点右键菜单
            var plusButtonMenu = $(go.Adornment, "Vertical",
            // menuButton("下一步：功能点", function(e, obj) { 
                //     ctrl.clickFun({ data: {"type": "Feature", "status": "add"} });
                //     $scope.obj = obj;
                //     $scope.category = "Feature"; }, function(o) { return true; }),
                // menuButton("下一步：结束", function(e, obj) { addNodeAndLink(e, obj, "End", { text: "End" }); }, function(o) { return true; })
                menuButton("下一步：展示页面", function(e, obj) {
                    ctrl.clickFun({ data: {"type": "Feature", "status": "add", "flag": "JUMP"} });
                    $scope.obj = obj;
                    $scope.category = "Feature";
                }, function(o) { return true; }),
                menuButton("下一步：页面交互", function(e, obj) { 
                    ctrl.clickFun({ data: {"type": "Feature", "status": "add", "flag": "UI"} });
                    $scope.obj = obj;
                    $scope.category = "Feature"; 
                }, function(o) { return true; }),
                menuButton("下一步：信息提交", function(e, obj) { 
                    ctrl.clickFun({ data: {"type": "Feature", "status": "add", "flag": "FORM"} });
                    $scope.obj = obj;
                    $scope.category = "Feature";
                }, function(o) { return true; }),
                menuButton("下一步：扫描枪扫描", function(e, obj) { 
                    ctrl.clickFun({ data: {"type": "Feature", "status": "add", "flag": "SCAN"} });
                    $scope.obj = obj;
                    $scope.category = "Feature";
                }, function(o) { return true; }),
                menuButton("下一步：判断", function(e, obj) { addNodeAndLink(e, obj, "Switch"); }, function(o) { return true; }),
                menuButton("下一步：处理描述", function(e, obj) { addNodeAndLink(e, obj, "Process"); }, function(o) { return true; }),
                menuButton("下一步：进入子流程", function(e, obj) { 
                    ctrl.clickFun({ data: {"type": "SubFlow", "status": "add"} });
                    $scope.obj = obj;
                    $scope.category = "SubFlow"; }, function(o) { return true; })
            );

            var nodeMenu = $(go.Adornment, "Vertical",
                // menuButton("变更成功能点", function(e, obj) { 
                //     $scope.e = e;
                //     $scope.partData = obj.part.data;
                //     $scope.category = "Feature";
                //     ctrl.clickFun({ data: {"type": "Feature", "status": "update"} });
                // }, function(o) { return ctrl.isReadonly ? false : (o.part.data.category != "Feature" && o.part.data.category != "Description"  ); }),
                // menuButton("变更成子流程", function(e, obj) { 
                //     $scope.e = e;
                //     $scope.partData = obj.part.data;
                //     $scope.category = "SubFlow";
                //     ctrl.clickFun({ data: {"type": "SubFlow", "status": "update"} });
                // }, function(o) { return ctrl.isReadonly ? false : (o.part.data.category != "SubFlow" && o.part.data.category != "Description" ); }),
                // menuButton("变更成判断", function(e, obj) { var data = {};
                //     $scope.changeNodeCategory(e, obj.part.data, "Switch", data) }, function(o) { return ctrl.isReadonly ? false : (o.part.data.category != "Switch" && o.part.data.category != "Description" ); }),
                // menuButton("变更成处理步骤", function(e, obj) { var data = {};
                //     $scope.changeNodeCategory(e, obj.part.data, "Process", data) }, function(o) { return ctrl.isReadonly ? false : (o.part.data.category != "Process" && o.part.data.category != "Description" ); }),
                // menuButton("变更输入输出", function(e, obj) { var data = {};
                //     $scope.changeNodeCategory(e, obj.part.data, "InputOutput", data) }, function(o) { return ctrl.isReadonly ? false : (o.part.data.category != "InputOutput" && o.part.data.category != "Description" ); }),
                menuButton("拉分支", function(e, obj) {
                    obj.part.data.category == "Feature" ? relateFeatureFun(obj.part.data, obj.part.data.feature_id) : relateFlowFun(obj.part.data, obj.part.data.flow_id);
                }, function(o) {
                    return ctrl.isReadonly ? false : (o.part.data.category == "Feature" && o.part.data.isRelate != 'isRelate' && $stateParams.reqId) ? true : (o.part.data.category == "SubFlow" && o.part.data.isRelate != 'isRelate' && $stateParams.reqId ? true : false);
                }),
                menuButton("重置", function(e, obj) {
                    obj.part.data.category == "Feature" ? reliveFeatureFun(obj.part.data, obj.part.data.feature_id) : reliveFlowFun(obj.part.data, obj.part.data.flow_id);
                }, function(o) {
                    return ctrl.isReadonly ? false : (o.part.data.category == "Feature" && o.part.data.isRelate == 'isRelate' && $stateParams.reqId) ? true : (o.part.data.category == "SubFlow" && o.part.data.isRelate == 'isRelate' && $stateParams.reqId ? true : false);
                }),
                menuButton("查看", function(e, obj) { 
                    if(obj.part.data.category == "Feature") {
                        showFeatureFun(obj.part.data, obj.part.data.feature_id);
                    }
                }, function(o) { 
                    return ctrl.isReadonly ? false : ((o.part.data.category == "Feature" && o.part.data.feature_info) ? true : false); 
                }),
                menuButton("删除节点", function(e, obj) { e.diagram.commandHandler.cutSelection(); }, function(o) { return ctrl.isReadonly ? false : true; })
            );

            function showFeatureFun(nodeData, id) {
                if(nodeData.feature_info) {
                    window.open("#/app/req/details/"+ nodeData.feature_info.reqId +"/"+ id +"/"+ nodeData.sys +"/"+ (nodeData.feature_info.reqId == '-1'), "_blank");
                }
            }

            // feature拉分支
            function relateFeatureFun(nodeData, id) {
                var url = matrix_product_url + "/feature/featureOccupy?reqId="+ ($stateParams.reqId || '-1') +"&id="+ id;
                $http.get(url).success(function(result) {
                    if(result.code == "0") {
                        myDiagram.model.setDataProperty(nodeData, "flag", nodeData.isNew+"/功能改动");
                        myDiagram.model.setDataProperty(nodeData, "nodeStatus", "/功能改动");
                        myDiagram.model.setDataProperty(nodeData, "isRelate", "isRelate");
                        $rootScope.toasterSuccessInfo("拉取分支成功！");
                    } else {
                        $rootScope.toasterErrorInfo(result.desc);
                    }
                });
            }

            // flow拉分支
            function relateFlowFun(nodeData, id) {
                var url = matrix_product_url + "/flow/relateWithFlow?flowId="+ id +"&reqId="+ $stateParams.reqId;
                $http.get(url).success(function(result) {
                    if(result.code == "0") {
                        myDiagram.model.setDataProperty(nodeData, "flag", nodeData.isNew+"/功能改动");
                        myDiagram.model.setDataProperty(nodeData, "nodeStatus", "/功能改动");
                        myDiagram.model.setDataProperty(nodeData, "isRelate", "isRelate");
                        ctrl.reloadFlowFun();
                        $rootScope.toasterSuccessInfo("拉取分支成功！");
                    } else {
                        $rootScope.toasterErrorInfo(result.desc);
                    }
                });
            }

            // feature重置
            function reliveFeatureFun(nodeData, id) {
                var url = matrix_product_url + "/feature/dissociateRelation?reqId="+ $stateParams.reqId +"&featureId="+ id;
                $http.get(url).success(function(result) {
                    if(result.code == "0") {
                        myDiagram.model.setDataProperty(nodeData, "flag", nodeData.isNew);
                        myDiagram.model.setDataProperty(nodeData, "nodeStatus", "");
                        myDiagram.model.setDataProperty(nodeData, "isRelate", "");
                        $rootScope.toasterSuccessInfo("重置成功！");
                    } else {
                        $rootScope.toasterErrorInfo(result.desc);
                    }
                });
            }

            // flow重置
            function reliveFlowFun(nodeData, id) {
                var url = matrix_product_url + "/flow/dissociateWithFlow?flowId="+ id +"&reqId="+ $stateParams.reqId;
                $http.get(url).success(function(result) {
                    if(result.code == "0") {
                        myDiagram.model.setDataProperty(nodeData, "flag", nodeData.isNew);
                        myDiagram.model.setDataProperty(nodeData, "nodeStatus", "");
                        myDiagram.model.setDataProperty(nodeData, "isRelate", "isAddNode");
                        ctrl.reloadFlowFun();
                        $rootScope.toasterSuccessInfo("重置成功！");
                    } else {
                        $rootScope.toasterErrorInfo(result.desc);
                    }
                });
            }

            $scope.changeNodeCategory = function(e, obj, category, data, nodeItem) {
                e.diagram.model.startTransaction('step-update');
                if(category == "Feature") {
                    if(nodeItem.type == "PROTOTYPE") {
                        featureName = nodeItem.path ? lodash.last(nodeItem.path.split("/")) : "未定义";
                    } else {
                        featureName = nodeItem.featureName || "未定义";
                    }
                }
                if(category == "Feature") {
                    obj.sys =  nodeItem.sys || "未定义";
                    obj.flag = "变更节点";
                    // obj.flag = setStatusByCn(nodeItem.status || "未定义");
                    obj.path = nodeItem.path || "未定义";
                    obj.featureName = featureName || "未定义";
                    obj.status = nodeItem.status || "";
                    obj.isOldItem = "newItem";
                    obj.name = nodeItem.featureName || "未定义";
                    obj.isRelate = nodeItem.isRelate || "未定义";
                    obj.desc = obj.desc == "请添加处理说明" ? "未定义" : obj.desc;
                    obj.id = nodeItem.id || "";
                    obj.feature_id = nodeItem.id || "";
                    obj.isNew = "变更节点";
                }
                if(category == "SubFlow") {
                    obj.flowName = nodeItem.flowname || "未定义";
                    obj.flag = "变更流程";
                    // obj.flag = setStatusByCn(nodeItem.status || "");
                    obj.path = nodeItem.path || "未定义";
                    obj.status = nodeItem.status || "";
                    obj.isOldItem = "newItem";
                    obj.name = nodeItem.flowname || "未定义";
                    obj.isRelate = nodeItem.isRelate || "未定义";
                    obj.desc = obj.desc == "请添加处理说明" ? "未定义" : obj.desc;
                    obj.id = nodeItem.id || "";
                    obj.flow_id = nodeItem.id || "";
                    obj.isNew = "变更流程";
                }
                if(category != "Feature" && category != "SubFlow") {
                    obj.desc = "请添加处理说明";
                }
                data = extend(obj, data, true);
                e.diagram.model.setCategoryForNodeData(data, category);
                shiftNodesToEmptySpaces();
                e.diagram.model.commitTransaction('step-update');
            }

            // 4.节点与连线模板
            var createRightPlusButton = function() {
                if (ctrl.isReadonly){
                    return {};
                }

                return $(go.Panel, "Auto",
                    // 4.3.1 这个形状的性质, 右对齐, 可以左连线(不允许toLinkable), 连接点是个圆点, 点击时关联addNodeAndLink函数.
                    {
                        alignment: go.Spot.Right,
                        portId: "R",
                        fromSpot: go.Spot.Right,
                        fromLinkable: true,
                        cursor: "pointer",
                        click: function(e, obj) {
                            // 只读模式下，禁止点击右侧添加按钮
                            if (ctrl.isReadonly) {
                                return false;
                            }
                            myDiagram.toolManager.contextMenuTool.showContextMenu(plusButtonMenu, obj);
                        }
                    },
                    $(go.Shape, "Circle", { width: 10, height: 10, fill: "white", stroke: "dodgerblue", strokeWidth: 1 }),
                    $(go.Shape, "PlusLine", { width: 7, height: 7, fill: null, stroke: "dodgerblue", strokeWidth: 2 })
                );
            }

            var createBottomPlusButton = function() {
                if (ctrl.isReadonly){
                    return {};
                }

                return $(go.Panel, "Auto",
                    // 4.3.1 这个形状的性质, 右对齐, 可以左连线(不允许toLinkable), 连接点是个圆点, 点击时关联addNodeAndLink函数.
                    {
                        alignment: go.Spot.Bottom,
                        portId: "B",
                        alignmentFocus: go.Spot.Bottom,
                        fromSpot: go.Spot.Bottom,
                        fromLinkable: true,
                        cursor: "pointer"
                    },
                    $(go.Shape, "Circle", { width: 6, height: 6, fill: "white", stroke: "dodgerblue" })
                );
            }

            var createLeftInputPort = function() {
                if (ctrl.isReadonly){
                    return {};
                }
                 
                return $(go.Panel, "Auto", { alignment: go.Spot.Left, portId: "L", toSpot: go.Spot.Left, toLinkable: true },
                    $(go.Shape, "Circle", { width: 4, height: 4, fill: "white", stroke: "gray" })
                    // $(go.Shape, "Circle", { width: 4, height: 4, fill: "dodgerblue", stroke: null })
                );
            }

            var createTopInputPort = function() {
                if (ctrl.isReadonly){
                    return {};
                }
                 
                return $(go.Panel, "Auto", { alignment: go.Spot.Top, portId: "T", toSpot: go.Spot.Left, toLinkable: true },
                    $(go.Shape, "Circle", { width: 4, height: 4, fill: "white", stroke: "gray" })
                    // $(go.Shape, "Circle", { width: 4, height: 4, fill: "dodgerblue", stroke: null })
                );
            }

            // 模板性质
            var defNode = function(config) {
                var defConfig = {
                    selectionAdorned: false,
                    textEditable: false,
                    locationObjectName: "BODY"
                };
                return extend(defConfig, config, true);
            }

            //  绑定模板坐标到data的loc属性.
            var bindLocation = function() {
                return new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify);
            }

            var bindSelection = function(selectedColor, unSelectedColor) {
                selectedColor = selectedColor == undefined ? "dodgerblue" : selectedColor;
                unSelectedColor = unSelectedColor == undefined ? graygradStyle : unSelectedColor;
                return new go.Binding("fill", "isSelected", function(s) { return s ? selectedColor : unSelectedColor; }).ofObject()
            }

            // 10. 连线的模板,可以控制连线的样式.
            myDiagram.linkTemplate = $(go.Link, // the whole link panel
                { 
                    curve: go.Link.Bezier,
                    corner: 5, toShortLength: 4,
                    relinkableFrom: true,
                    relinkableTo: false,   // 是否允许点击切换到端
                    reshapable: true,
                    resegmentable: true
                },
                $(go.Shape, { strokeWidth: 1.5 }),
                $(go.Shape, { toArrow: "OpenTriangle", stroke: "black"}),
                $(go.TextBlock, "+",
                    {
                      font: "bold 13pt Source Sans Pro, sans-serif",
                    //   segmentIndex: 0,  // 未设置时，默认为线的中点
                      segmentOffset: new go.Point(NaN, NaN),
                      isMultiline: false,
                      editable: true,
                      stroke: "green",
                    },
                    new go.Binding("text", "text").makeTwoWay())
            );

            // 6.定义模板库, 所有模板都是从spot克隆再定制来的.

            // 6.1 Start 模板
            myDiagram.nodeTemplateMap.add(
                "Start",
                $(go.Node, "Spot",
                    defNode(),
                    bindLocation(),
                    $(go.Panel, "Auto", { name: "BODY" },
                        $(go.Shape, "Circle", { fill: graygradStyle, stroke: "gray", minSize: new go.Size(20, 20) },
                            bindSelection()),
                        $(go.TextBlock, textBox(),
                            new go.Binding("text", "text"))
                    ),
                    // output port
                    createRightPlusButton()
                    // createBottomPlusButton()
                )
            );

            // 6.2.End节点.
            myDiagram.nodeTemplateMap.add(
                "End",
                $(go.Node, "Spot",
                    defNode(),
                    bindLocation(),
                    $(go.Panel, "Auto", { name: "BODY" },
                        $(go.Shape, "StopSign", { fill: graygradStyle, stroke: "gray", minSize: new go.Size(48, 48) },
                            bindSelection()),
                        $(go.TextBlock, textBox(),
                            new go.Binding("text", "text"))
                    ),
                    // createTopInputPort(),
                    createLeftInputPort()
                )
            );

            // 表格标题栏样式.
            var tableTile = function() {
                var defConfig = textBox({ font: "bold 13px sans-serif" });
                return extend(defConfig, { row: 0, column: 0, columnSpan: 2, text: "" }, true);
            }

            // 表格样式
            var rowLable = function(r, c, text, editor) {
                var defConfig = textBox();
                return extend(defConfig, { row: r, column: c, text: text }, true);
            }

            // 6.3 Feature 模板
            myDiagram.nodeTemplateMap.add(
                "Feature", $(go.Node, "Spot", 
                    defNode(),
                    bindLocation(),
                    { name: "X", selectionAdorned: false, textEditable: true, locationObjectName: "BODY" }, { contextMenu: nodeMenu },
                    // the main body consists of a Rectangle surrounding the text
                    $(go.Panel, "Auto", { name: "BODY" },
                        $(go.Shape, "RoundedRectangle", { fill: graygradStyle, minSize: new go.Size(8, 8) },
                            bindSelection(),
                            new go.Binding("stroke", "isHighlighted", function(h) {
                                return h ? "red" : "gray";
                            }).ofObject()),

                        $(go.Panel, "Table", { alignment: go.Spot.TopCenter, height: 40, margin: new go.Margin(0, 10, 0, 15) },
                            new go.Binding("rowCount", "w"),
                            $(go.TextBlock, rowLable(1, 1), new go.Binding("text", "featureKind", function(kind) {
                                var featureKind = "";
                                switch(kind) {
                                    case "JUMP":
                                        featureKind = "【展示页面】";
                                        break;
                                    case "UI":
                                        featureKind = "【页面交互】";
                                        break;
                                    case "FORM":
                                        featureKind = "【信息提交】";
                                        break;
                                    case "SCAN":
                                        featureKind = "【扫描枪扫描】";
                                        break;
                                }
                                return featureKind;
                            })),
                            $(go.TextBlock, rowLable(1, 1), { margin: new go.Margin(0, 10, 0, 85) },
                                new go.Binding("stroke", "isOldItem", function(h) {
                                    return h == "newItem" ? "#912CEE" : "black";
                                }).makeTwoWay(),
                                new go.Binding("stroke", "isRelate", function(r) {
                                    return r == "isRelate" ? "green" : (r == "oldItem" ? "black" : "#912CEE");
                                }).makeTwoWay(),
                                new go.Binding("text", "featureName"))
                        ),
                        $("PanelExpanderButton", "FEATURE", { row: 0, alignment: go.Spot.TopLeft ,margin: new go.Margin(10, 0, 0, 5)}),
                        $(go.Panel, "Table",{ visible: false }, { defaultAlignment: go.Spot.Left, name: "FEATURE", height: 80, margin: new go.Margin(40, 0, 0, 0) },
                            new go.Binding("rowCount", "w"),
                            $(go.TextBlock, rowLable(1, 0, "变更:")),
                            $(go.TextBlock, rowLable(1, 1), new go.Binding("text", "flag")),
                            $(go.TextBlock, rowLable(2, 0, "菜单:")),
                            $(go.TextBlock, rowLable(2, 1), new go.Binding("text", "path").makeTwoWay()),
                            $(go.TextBlock, rowLable(3, 0, "系统:")),
                            $(go.TextBlock, rowLable(3, 1), new go.Binding("text", "sys").makeTwoWay())
                            // $(go.TextBlock, rowLable(4, 0, "备注:")),
                            // $(go.TextBlock, rowLable(4, 1), { stroke: "blue", editable: true, maxSize: new go.Size(160, NaN) }, new go.Binding("text", "desc").makeTwoWay())
                        )
                    ),
                    createRightPlusButton(),
                    createBottomPlusButton(),
                    createLeftInputPort(),
                    createTopInputPort()
                ));

            // 6.3 SubFlow 模板
            myDiagram.nodeTemplateMap.add(
                "SubFlow", $(go.Node, "Spot", 
                    defNode(),
                    bindLocation(),
                    { name: "X", selectionAdorned: false, textEditable: true, locationObjectName: "BODY" }, { contextMenu: nodeMenu },
                    // the main body consists of a Rectangle surrounding the text
                    $(go.Panel, "Auto", { name: "BODY" },
                        $(go.Shape, "MultiProcess", { fill: graygradStyle, minSize: new go.Size(8, 8) },
                            bindSelection(),
                            new go.Binding("stroke", "isHighlighted", function(h) {
                                return h ? "red" : "gray";
                            }).ofObject()),
                        $(go.Panel, "Table", { alignment: go.Spot.TopCenter, height: 40, margin: new go.Margin(0, 10, 0, 15) },
                            new go.Binding("rowCount", "w"),
                            $(go.TextBlock, tableTile(),
                                new go.Binding("stroke", "isOldItem", function(h) {
                                    return h == "newItem" ? "#912CEE" : "black";
                                }).makeTwoWay(),
                                new go.Binding("stroke", "isRelate", function(r) {
                                    return r == "isRelate" ? "green" : (r == "oldItem" ? "black" : "#912CEE");
                                }).makeTwoWay(),
                                new go.Binding("text", "flowName"))
                        ),

                        $("PanelExpanderButton", "FLOW", { row: 0, alignment: go.Spot.TopLeft, margin: new go.Margin(10, 0, 0, 5) }),
                        $(go.Panel, "Table", { visible: false },{ name: "FLOW", alignment: go.Spot.Left, margin: new go.Margin(40, 0, 0, 0) },
                            new go.Binding("rowCount", "w"),
                            $(go.TextBlock, rowLable(1, 0, "变更:")),
                            $(go.TextBlock, rowLable(1, 1), new go.Binding("text", "flag")),
                            $(go.TextBlock, rowLable(2, 0, "场景:")),
                            $(go.TextBlock, rowLable(2, 1), new go.Binding("text", "path"))
                            // $(go.TextBlock, rowLable(3, 0, "备注:")),
                            // $(go.TextBlock, rowLable(3, 1), { stroke: "blue", editable: true, maxSize: new go.Size(160, NaN) }, new go.Binding("text", "desc").makeTwoWay())
                        )
                    ),
                    createRightPlusButton(),
                    createLeftInputPort(),
                    createBottomPlusButton(),
                    createTopInputPort()
                ));


            var editableTextBox = function() {
                return textBox({ editable: true, font: "bold 14px sans-serif", margin: new go.Margin(2, 2, 2, 2), alignment: go.Spot.Center, maxSize: new go.Size(160, NaN)});
            }

            //通用node形状构造器
            var createNodeShape = function(category, hiddenIo, fill) {
                fill = (fill == undefined) ? graygradStyle : fill;

                return $(go.Node, "Spot", { selectionAdorned: false, textEditable: true, locationObjectName: "BODY", contextMenu: nodeMenu },
                    new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
                    // the main body consists of a Rectangle surrounding the text
                    $(go.Panel, "Auto", { name: "BODY" },
                        $(go.Shape, category, { fill: fill, stroke: "gray", minSize: new go.Size(120, 80) },
                            bindSelection("dodgerblue", fill)),
                        $(go.TextBlock, editableTextBox(),
                            new go.Binding("stroke", "isHighlighted", function(h) {
                                return h ? "red" : "black";
                            }).ofObject(),
                            new go.Binding("text", "desc").makeTwoWay())
                    ),
                    hiddenIo ? {} : createRightPlusButton(),
                    hiddenIo ? {} : createLeftInputPort(),
                    hiddenIo ? {} : createBottomPlusButton(),
                    hiddenIo ? {} : createTopInputPort()
                );
            }

            // 6.4.Switch节点.
            myDiagram.nodeTemplateMap.add("Switch", createNodeShape("Diamond"));

            // 6.4.InputOutput节点.
            myDiagram.nodeTemplateMap.add("InputOutput", createNodeShape("output"));

            // 6.4.Process节点.
            myDiagram.nodeTemplateMap.add("Process", createNodeShape("RoundedRectangle"));

            // 6.4.Description节点.
            myDiagram.nodeTemplateMap.add("Description", createNodeShape("RoundedRectangle", true, "lightyellow"));

            // 5.节点构造方法
            function addNodeAndLink(e, obj, category, data) {
                var fromNode = obj.part;
                var diagram = fromNode.diagram;

                category = category == undefined ? "Process" : category;

                var p = fromNode.location.copy();
                p.x += diagram.toolManager.draggingTool.gridSnapCellSize.width;

                if (category != "Feature" && category != "SubFlow") {
                    data = extend({ category: category, desc: "请添加处理说明", loc: go.Point.stringify(p) }, data, true);
                } else {
                    data = extend({ loc: go.Point.stringify(p) }, data, true);
                }
                // data: 节点模板的数据格式
                doAddNode(fromNode, data, true);
                myDiagram.toolManager.contextMenuTool.hideContextMenu();
            }

            function addDescription(e, obj) {
                var fromNode = obj.part;

                doAddNode(fromNode, { category: "Description", desc: "请添加注释" })
            }

            function doAddNode(fromNode, data, needLink) {
                var diagram = fromNode.diagram;
                //8.1 开启一个事务
                diagram.startTransaction("Add State");
                var model = diagram.model;
                model.addNodeData(data);
                if (needLink) {
                    var linkdata = {
                        from: model.getKeyForNodeData(fromNode.data),
                        to: model.getKeyForNodeData(data),
                        fromPort: "R",
                        toPort: "L"
                    };
                    // and add the link data to the model
                    model.addLinkData(linkdata);
                }
                var newnode = diagram.findNodeForData(data);
                diagram.select(newnode);
                var p = fromNode.location.copy();

                if (!fromNode || fromNode.data.category == undefined) {
                    //do nothing...
                } else if (fromNode.data.category == "Feature") {
                    p.x += 80;
                } else if (fromNode.data.category == "Start") {
                    p.x += 80;
                } else {
                    p.x += 80;
                }
                newnode.location = diagram.toolManager.draggingTool.computeMove(newnode, p);
                shiftNodesToEmptySpaces();
                diagram.commitTransaction("Add State");
            }

            // 11.  配置连线的特效.
            var OldTarget = null;

            function commonLinkingToolInit(tool) {
                // 拖拽中, 还没有落地的连线样式.
                tool.temporaryLink =
                    $(go.Link, { layerName: "Tool" },
                        $(go.Shape, temporaryLinkStyle));
                // 更改退拽中的临时连线的两头的样式, 默认的很丑是个紫色的小方块, 这个配置成空的了.
                tool.temporaryFromPort.figure = "Circle";
                tool.temporaryFromPort.stroke = null;
                tool.temporaryFromPort.strokeWidth = 0;
                tool.temporaryToPort.figure = "Circle";
                tool.temporaryToPort.stroke = null;
                tool.temporaryToPort.strokeWidth = 0;
                // 连线的高光与选中函数, 记录上一次鼠标选中的对象, 将其高亮或者回复正常.
                tool.portTargeted = function(realnode, realport, tempnode, tempport, toend) {
                    if (realport === null) { // no valid port nearby
                        if (OldTarget) {
                            OldTarget.scale = 1.0;
                            OldTarget = null;
                        }
                    } else if (toend) {
                        if (OldTarget !== realport) {
                            OldTarget = realport;
                            realport.scale = 1.3; // highlight by enlarging
                        }
                    }
                };
            }

            var leftTool = myDiagram.toolManager.linkingTool;
            commonLinkingToolInit(leftTool);
            // 不允许从node的左边开始拖拽连线
            leftTool.direction = go.LinkingTool.ForwardsOnly;

            var rightTool = myDiagram.toolManager.relinkingTool;
            commonLinkingToolInit(rightTool);
            rightTool.toHandleArchetype =
                $(go.Shape, { isPanelMain: true, fill: null, stroke: "dodgerblue", strokeWidth: 5 });

            myDiagram.toolManager.draggingTool = new DragLinkingTool();

            // 添加一个重要的控制能力, 将所选节点移动到一个不覆盖其他节点的位置, 拖拽放开时会调用它.
            myDiagram.addDiagramListener("SelectionMoved", shiftNodesToEmptySpaces);

            function shiftNodesToEmptySpaces() {
                myDiagram.selection.each(function(node) {
                    console.log("shiftNodesToEmptySpaces");
                    console.log(node);
                    if (!(node instanceof go.Node)) return;
                    // look for Parts overlapping the node
                    while (true) {
                        var exist = myDiagram.findObjectsIn(node.actualBounds,
                            // only consider Parts
                            function(obj) { return obj.part; },
                            // ignore Links and the dropped node itself
                            function(part) { return part instanceof go.Node && part !== node; },
                            // check for any overlap, not complete containment
                            true).first();
                        if (exist === null) break;
                        // try shifting down beyond the existing node to see if there's empty space
                        node.position = new go.Point(node.actualBounds.x, exist.actualBounds.bottom + ColumeInterval);
                    }
                });
            }

            // 2 画布右键菜单
            var backgroudMenu = $(go.Adornment, "Vertical", menuButton("添加注释", addDescription, function(o) { return true }));
            if(!ctrl.isReadonly) {
                myDiagram.contextMenu = backgroudMenu;
            }

            //*****************修改数据**********cwk--update */
            $scope.propAddNodeAndLink = function(obj, category, nodeItem) {
                console.log("这是components中的方法");
                console.log(nodeItem);
                if (!nodeItem) {
                    $rootScope.toasterWarningInfo("引入节点数据错误，请重试！");
                    return false;
                }
                var defaultNodeData = { desc: "未定义", category: category };
                var featureName = "";
                if(category == "Feature") {
                    if(nodeItem.type == "PROTOTYPE") {
                        featureName = nodeItem.path ? lodash.last(nodeItem.path.split("/")) : "未定义";
                    } else {
                        featureName = nodeItem.featureName || "未定义";
                    }
                }
                var featureNodeData = {
                    category: category,
                    sys: nodeItem.sys || "未定义",
                    flag: nodeItem.isNew || "未定义",
                    // flag: setStatusByCn(nodeItem.status || ""),
                    path: nodeItem.path || "未定义",
                    featureName: featureName || "未定义",
                    status: nodeItem.status || "",
                    name: nodeItem.featureName || "未定义",
                    isRelate: nodeItem.isRelate || "未定义",
                    isOldItem: nodeItem.isOldItem || "oldItem",
                    isNew: nodeItem.isNew || "新增节点",
                    feature_id: nodeItem.feature_id || "",
                    featureKind: nodeItem.featureKind || "JUMP",
                    feature_info: nodeItem.feature_info || "",
                    desc: "未定义",
                    id: nodeItem.id || "",
                    loc: nodeItem.loc || ""
                };
                var subFlowNodeData = {
                    category: category,
                    flowName: nodeItem.flowname || "未定义",
                    status: nodeItem.status || "",
                    path: nodeItem.path || "未定义",
                    flag: nodeItem.isNew || "未定义",
                    // flag: setStatusByCn(nodeItem.status || ""),
                    name: nodeItem.flowname || "未定义",
                    isRelate: nodeItem.isRelate || "未定义",
                    isOldItem: nodeItem.isOldItem || "oldItem",
                    isNew: nodeItem.isNew || "新增流程",
                    flow_id: nodeItem.flow_id || "",
                    desc: "未定义",
                    id: nodeItem.id || "",
                    loc: nodeItem.loc || ""
                };
                var data = category == "Feature" ? featureNodeData : (category == "SubFlow" ? subFlowNodeData : defaultNodeData);
                addNodeAndLink("", obj, category, data);
            };

            function setStatusByCn(status) {
                var statusName = "";
                switch(status) {
                    case "ONLINE": 
                        statusName = "无";
                        break;
                    case "ADD": 
                        statusName = "新增";
                        break;
                    case "UPDATE": 
                        statusName = "修改";
                        break;
                    default: 
                        statusName = "未定义";
                }
                return statusName;
            }

            ctrl.$onChanges = function(data) {
                //版本号
                if (ctrl.isShowVersion) {
                    var versionNum = ctrl.version.version ? "(" + ctrl.version.version + ")" : "";
                    var versionType = ctrl.paramsType == "current" ? "【当前】" : "【线上】";
                    ctrl.versionName = (ctrl.version.name || "") + (versionNum || versionType);
                }
                ctrl.isOnline = ctrl.paramsType == "current" ? false : true;
                $timeout(function() {
                    var nodeDatas = {
                        "class": "go.GraphLinksModel",
                        "linkFromPortIdProperty": "fromPort",
                        "linkToPortIdProperty": "toPort",
                        "nodeDataArray": ctrl.datas.nodeDataArray,
                        "linkDataArray": ctrl.datas.linkDataArray
                    };
                    myDiagram.model = go.Model.fromJson(nodeDatas);
                    // myDiagram.model = new go.GraphLinksModel(ctrl.datas.nodeDataArray, ctrl.datas.linkDataArray);
                }, 100)
            }

            // myDiagram.model = new go.GraphLinksModel(ctrl.datas.nodeDataArray, ctrl.datas.linkDataArray);
            var datas = {
                "class": "go.GraphLinksModel",
                "linkFromPortIdProperty": "fromPort",
                "linkToPortIdProperty": "toPort",
                "nodeDataArray": ctrl.datas.nodeDataArray,
                "linkDataArray": ctrl.datas.linkDataArray
            };
            myDiagram.model = go.Model.fromJson(datas);

            // 保存时获取diagram中的全部数据
            $scope.save = function() {
                $scope.flowData = myDiagram.model.toJson();
            }

            //点击事件
            myDiagram.addDiagramListener("ObjectSingleClicked", function(e) {
                $scope.selectNode = e.subject.part;
                ctrl.blur({ data: e.subject.part.data });
            });

            $scope.searchNode = function(input) {
                myDiagram.startTransaction("highlight search");

                if (input) {
                    var regex = new RegExp(input, "i");
                    var results = myDiagram.findNodesByExample({ featureName: regex }, { sys: regex }, { flowName: regex }, { path: regex }, { desc: regex });
                    myDiagram.highlightCollection(results);
                    if (results.count > 0) myDiagram.centerRect(results.first().actualBounds);
                } else {
                    myDiagram.clearHighlighteds();
                }
                myDiagram.commitTransaction("highlight search");
            }

            // 双向数据绑定的时候，需要把$scope设置给dirgramScope
            if (ctrl.diagramScope) {
                ctrl.diagramScope = $scope;
            }
            //********************end修改数据******* */
            // myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
            myDiagram.layoutDiagram(true);
        }, 100);
    }
});